---
title: Tilores
---

This notebook covers how to get started with the [Tilores](/oss/integrations/providers/tilores) tools.
For a more complex example you can checkout our [customer insights chatbot example](https://github.com/tilotech/identity-rag-customer-insights-chatbot).

## Overview

### Integration details

| Class | Package | Serializable | JS support |  Version |
| :--- | :--- | :---: | :---: | :---: |
| TiloresTools | [tilores-langchain](https://pypi.org/project/tilores-langchain/) | ❌ | ❌ |  ![PyPI - Version](https://img.shields.io/pypi/v/tilores-langchain?style=flat-square&label=%20) |

## Setup

The integration requires the following packages:

```python
%pip install --quiet -U tilores-langchain langchain
```

```output
Note: you may need to restart the kernel to use updated packages.
```

### Credentials

To access Tilores, you need to [create and configure an instance](https://app.tilores.io). If you prefer to test out Tilores first, you can use the [read-only demo credentials](https://github.com/tilotech/identity-rag-customer-insights-chatbot?tab=readme-ov-file#1-configure-customer-data-access).

```python
import os

os.environ["TILORES_API_URL"] = "<api-url>"
os.environ["TILORES_TOKEN_URL"] = "<token-url>"
os.environ["TILORES_CLIENT_ID"] = "<client-id>"
os.environ["TILORES_CLIENT_SECRET"] = "<client-secret>"
```

## Instantiation

Here we show how to instantiate an instance of the Tilores tools:

```python
from tilores import TiloresAPI
from tilores_langchain import TiloresTools

tilores = TiloresAPI.from_environ()
tilores_tools = TiloresTools(tilores)
search_tool = tilores_tools.search_tool()
edge_tool = tilores_tools.edge_tool()
```

## Invocation

The parameters for the `tilores_search` tool are dependent on the [configured schema](https://docs.tilotech.io/tilores/schema/) within Tilores. The following examples will use the schema for the demo instance with generated data.

### [Invoke directly with args](/oss/langchain/tools)

The following example searches for a person called Sophie Müller in Berlin. The Tilores data contains multiple such persons and returns their known email addresses and phone numbers.

```python
result = search_tool.invoke(
    {
        "searchParams": {
            "name": "Sophie Müller",
            "city": "Berlin",
        },
        "recordFieldsToQuery": {
            "email": True,
            "phone": True,
        },
    }
)
print("Number of entities:", len(result["data"]["search"]["entities"]))
for entity in result["data"]["search"]["entities"]:
    print("Number of records:", len(entity["records"]))
    print(
        "Email Addresses:",
        [record["email"] for record in entity["records"] if record.get("email")],
    )
    print(
        "Phone Numbers:",
        [record["phone"] for record in entity["records"] if record.get("phone")],
    )
```

```output
Number of entities: 3
Number of records: 3
Email Addresses: ['s.mueller@newcompany.de', 'sophie.mueller@email.de']
Phone Numbers: ['30987654', '30987654', '30987654']
Number of records: 5
Email Addresses: ['mueller.sophie@uni-berlin.de', 'sophie.m@newshipping.de', 's.mueller@newfinance.de']
Phone Numbers: ['30135792', '30135792']
Number of records: 2
Email Addresses: ['s.mueller@company.de']
Phone Numbers: ['30123456', '30123456']
```

If we're interested how the records from the first entity are related, we can use the edge_tool. Note that the Tilores entity resolution engine figured out the relation between those records automatically. Please refer to the [edge documentation](https://docs.tilotech.io/tilores/rules/#edges) for more details.

```python
edge_result = edge_tool.invoke(
    {"entityID": result["data"]["search"]["entities"][0]["id"]}
)
edges = edge_result["data"]["entity"]["entity"]["edges"]
print("Number of edges:", len(edges))
print("Edges:", edges)
```

```output
Number of edges: 7
Edges: ['e1f2g3h4-i5j6-k7l8-m9n0-o1p2q3r4s5t6:f2g3h4i5-j6k7-l8m9-n0o1-p2q3r4s5t6u7:L1', 'e1f2g3h4-i5j6-k7l8-m9n0-o1p2q3r4s5t6:g3h4i5j6-k7l8-m9n0-o1p2-q3r4s5t6u7v8:L4', 'e1f2g3h4-i5j6-k7l8-m9n0-o1p2q3r4s5t6:f2g3h4i5-j6k7-l8m9-n0o1-p2q3r4s5t6u7:L2', 'f2g3h4i5-j6k7-l8m9-n0o1-p2q3r4s5t6u7:g3h4i5j6-k7l8-m9n0-o1p2-q3r4s5t6u7v8:L1', 'f2g3h4i5-j6k7-l8m9-n0o1-p2q3r4s5t6u7:g3h4i5j6-k7l8-m9n0-o1p2-q3r4s5t6u7v8:L4', 'e1f2g3h4-i5j6-k7l8-m9n0-o1p2q3r4s5t6:g3h4i5j6-k7l8-m9n0-o1p2-q3r4s5t6u7v8:L1', 'e1f2g3h4-i5j6-k7l8-m9n0-o1p2q3r4s5t6:f2g3h4i5-j6k7-l8m9-n0o1-p2q3r4s5t6u7:L4']
```

### [Invoke with ToolCall](/oss/langchain/tools)

We can also invoke the tool with a model-generated ToolCall, in which case a ToolMessage will be returned:

```python
# This is usually generated by a model, but we'll create a tool call directly for demo purposes.
model_generated_tool_call = {
    "args": {
        "searchParams": {
            "name": "Sophie Müller",
            "city": "Berlin",
        },
        "recordFieldsToQuery": {
            "email": True,
            "phone": True,
        },
    },
    "id": "1",
    "name": search_tool.name,
    "type": "tool_call",
}
search_tool.invoke(model_generated_tool_call)
```

```output
ToolMessage(content='{"data": {"search": {"entities": [{"id": "9601cf3b-e85f-46ab-aaa8-ffb8b46f1c5b", "hits": {"c3d4e5f6-g7h8-i9j0-k1l2-m3n4o5p6q7r8": ["L1"]}, "records": [{"email": "", "phone": "30123456"}, {"email": "s.mueller@company.de", "phone": "30123456"}]}, {"id": "03da2e11-0aa2-4d17-8aaa-7b32c52decd9", "hits": {"e1f2g3h4-i5j6-k7l8-m9n0-o1p2q3r4s5t6": ["L1"], "g3h4i5j6-k7l8-m9n0-o1p2-q3r4s5t6u7v8": ["L1"]}, "records": [{"email": "s.mueller@newcompany.de", "phone": "30987654"}, {"email": "", "phone": "30987654"}, {"email": "sophie.mueller@email.de", "phone": "30987654"}]}, {"id": "4d896fb5-0d08-4212-a043-b5deb0347106", "hits": {"j6k7l8m9-n0o1-p2q3-r4s5-t6u7v8w9x0y1": ["L1"], "l8m9n0o1-p2q3-r4s5-t6u7-v8w9x0y1z2a3": ["L1"], "m9n0o1p2-q3r4-s5t6-u7v8-w9x0y1z2a3b4": ["L1"], "n0o1p2q3-r4s5-t6u7-v8w9-x0y1z2a3b4c5": ["L1"]}, "records": [{"email": "mueller.sophie@uni-berlin.de", "phone": ""}, {"email": "sophie.m@newshipping.de", "phone": ""}, {"email": "", "phone": "30135792"}, {"email": "", "phone": ""}, {"email": "s.mueller@newfinance.de", "phone": "30135792"}]}]}}}', name='tilores_search', tool_call_id='1')
```

## Chaining

We can use our tool in a chain by first binding it to a [tool-calling model](/oss/langchain/tools/) and then calling it:

<ChatModelTabs customVarName="llm" />

```python
# | output: false
# | echo: false

# !pip install -qU langchain langchain-openai
from langchain.chat_models import init_chat_model

llm = init_chat_model(model="gpt-4o", model_provider="openai")
```

```python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableConfig, chain

prompt = ChatPromptTemplate(
    [
        ("system", "You are a helpful assistant."),
        ("human", "{user_input}"),
        ("placeholder", "{messages}"),
    ]
)

# specifying tool_choice will force the model to call this tool.
llm_with_tools = llm.bind_tools([search_tool], tool_choice=search_tool.name)

llm_chain = prompt | llm_with_tools


@chain
def tool_chain(user_input: str, config: RunnableConfig):
    input_ = {"user_input": user_input}
    ai_msg = llm_chain.invoke(input_, config=config)
    tool_msgs = search_tool.batch(ai_msg.tool_calls, config=config)
    return llm_chain.invoke({**input_, "messages": [ai_msg, *tool_msgs]}, config=config)


tool_chain.invoke("Tell me the email addresses from Sophie Müller from Berlin.")
```

## API reference

For detailed documentation of all Tilores features and configurations head to the official documentation: [docs.tilotech.io/tilores/](https://docs.tilotech.io/tilores/)
